package view.contents;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import model.Content;
import model.Player;

import com.xuggle.xuggler.Global;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.Utils;

import config.Debug;
import controller.ScheduleController;

public class XuggleVideoBox extends ContentBox {

	private String content;
	private Content c;
	private String filename;
	private int width;
	private int height;
	private boolean isSuccessfullyInitialized = false;
	private boolean willBeRemoved;
	private BufferedImage videoFrame;

	public XuggleVideoBox(Content c) {
		this.c = c;
		this.filename = c.getContent();
		// this.setBounds(0,0,200,200);
		this.width = 400;
		this.height = 300;
		this.init();
	}

	public XuggleVideoBox(Content c, int width, int height) {
		this.c = c;
		this.filename = c.getContent();
		this.width = width;
		this.height = height;
		this.init();

	}

	private void init() {
		this.setLayout(new BorderLayout());
		this.setIgnoreRepaint(true);
	}

	@Override
	public void paintComponent(Graphics g) {

		if (this.videoFrame != null) {
			g.drawImage(videoFrame, 0, 0, null);
		} else {
			System.out.println("video frame is null");
		}
	}

	@Override
	public String getContent() {
		// TODO Auto-generated method stub
		return this.content;
	}

	@Override
	public void cleanUp() {
		// TODO Auto-generated method stub
		this.willBeRemoved = true;

	}

	@Override
	public void reinitialize() {
		// TODO Auto-generated method stub

	}

	@Override
	public void initialize() {
		// TODO Auto-generated method stub
		Thread t = new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					XuggleVideoBox.this.play();
				} catch (Exception e) {
					XuggleVideoBox.this.c.setHasUpdate(true);
					Debug.log("Xuggler: can't play video " + XuggleVideoBox.this.filename + " " + e.getMessage(), Debug.ERROR_LEVEL);
					Debug.msg("Xuggler: can't play video " + XuggleVideoBox.this.filename + " " + e.getMessage());
					synchronized (ScheduleController.getVideoLock()) {
						ScheduleController.getVideoLock().notifyAll();
					}
				}

			}
		});
		t.start();
	}

	public void play() throws Exception {
		// Let's make sure that we can actually convert video pixel formats.
		if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION)) {
			throw new RuntimeException("you must install the GPL version" + " of Xuggler (with IVideoResampler support) for " + "this demo to work");
		}

		// Create a Xuggler container object
		IContainer container = IContainer.make();

		// Open up the container
		if (container.open(config.Settings.getVideoDirectory() + filename, IContainer.Type.READ, null) < 0) {
			String msg = "Xuggler could not open file: " + filename;
			Debug.log(msg, Debug.ERROR_LEVEL);
			throw new IllegalArgumentException(msg);
		}

		// query how many streams the call to open found
		int numStreams = container.getNumStreams();

		// and iterate through the streams to find the first video stream
		int videoStreamId = -1;
		IStreamCoder videoCoder = null;
		for (int i = 0; i < numStreams; i++) {
			// Find the stream object
			IStream stream = container.getStream(i);
			// Get the pre-configured decoder that can decode this stream;
			IStreamCoder coder = stream.getStreamCoder();

			if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
				videoStreamId = i;
				videoCoder = coder;
				break;
			}
		}
		if (videoStreamId == -1)
			throw new RuntimeException("could not find video stream in container: " + filename);

		/*
		 * Now we have found the video stream in this file. Let's open up our
		 * decoder so it can do work.
		 */
		if (videoCoder.open() < 0)
			throw new RuntimeException("could not open video decoder for container: " + filename);

		IVideoResampler resampler = null;
		if (videoCoder.getPixelType() != IPixelFormat.Type.BGR24) {
			// if this stream is not in BGR24, we're going to need to
			// convert it. The VideoResampler does that for us.
			resampler = IVideoResampler.make(this.width, this.height, IPixelFormat.Type.BGR24, videoCoder.getWidth(), videoCoder.getHeight(),
					videoCoder.getPixelType());
			if (resampler == null)
				throw new RuntimeException("could not create color space " + "resampler for: " + filename);
		}

		/*
		 * Now, we start walking through the container looking at each packet.
		 */
		IPacket packet = IPacket.make();
		long firstTimestampInStream = Global.NO_PTS;
		long systemClockStartTime = 0;

		long duration = (int) container.getDuration();
		if (duration != Global.NO_PTS) {

			int pauseTime = Player.getInstance().getCurrentPlaylistRecord().getPauseTime();
			if (pauseTime == 0) {
				pauseTime = (int) (duration / 1000 / 1000);
			} else if (pauseTime == -1) {
				pauseTime = 0;
			}
			Player.getInstance().getCurrentPlaylistRecord().setPauseTime(pauseTime);
			Debug.log("video pause time is " + duration + " " + pauseTime, Debug.SYSTEM_LEVEL);
			Debug.log("Video started at " + System.currentTimeMillis(), Debug.SYSTEM_LEVEL);
			synchronized (ScheduleController.getVideoLock()) {
				ScheduleController.getVideoLock().notifyAll();
			}
		}
		while (container.readNextPacket(packet) >= 0 && (!this.willBeRemoved())) {
			/*
			 * Now we have a packet, let's see if it belongs to our video stream
			 */
			if (packet.getStreamIndex() == videoStreamId) {
				/*
				 * We allocate a new picture to get the data out of Xuggler
				 */
				IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight());

				int offset = 0;
				while (offset < packet.getSize()) {
					/*
					 * Now, we decode the video, checking for any errors.
					 */
					int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset);
					if (bytesDecoded < 0)
						throw new RuntimeException("got error decoding video in: " + filename);
					offset += bytesDecoded;

					/*
					 * Some decoders will consume data in a packet, but will not
					 * be able to construct a full video picture yet. Therefore
					 * you should always check if you got a complete picture
					 * from the decoder
					 */
					if (picture.isComplete()) {
						IVideoPicture newPic = picture;
						/*
						 * If the resampler is not null, that means we didn't
						 * get the video in BGR24 format and need to convert it
						 * into BGR24 format.
						 */
						if (resampler != null) {
							// we must resample
							newPic = IVideoPicture.make(resampler.getOutputPixelFormat(), this.width, this.height);
							if (resampler.resample(newPic, picture) < 0)
								throw new RuntimeException("could not resample video from: " + filename);
						}
						if (newPic.getPixelType() != IPixelFormat.Type.BGR24)
							throw new RuntimeException("could not decode video" + " as BGR 24 bit data in: " + filename);

						/**
						 * We could just display the images as quickly as we
						 * decode them, but it turns out we can decode a lot
						 * faster than you think.
						 * 
						 * So instead, the following code does a poor-man's
						 * version of trying to match up the frame-rate
						 * requested for each IVideoPicture with the system
						 * clock time on your computer.
						 * 
						 * Remember that all Xuggler IAudioSamples and
						 * IVideoPicture objects always give timestamps in
						 * Microseconds, relative to the first decoded item. If
						 * instead you used the packet timestamps, they can be
						 * in different units depending on your IContainer, and
						 * IStream and things can get hairy quickly.
						 */
						if (firstTimestampInStream == Global.NO_PTS) {
							// This is our first time through
							firstTimestampInStream = picture.getTimeStamp();
							// get the starting clock time so we can hold up
							// frames
							// until the right time.
							systemClockStartTime = System.currentTimeMillis();
						} else {
							long systemClockCurrentTime = System.currentTimeMillis();
							long millisecondsClockTimeSinceStartofVideo = systemClockCurrentTime - systemClockStartTime;
							// compute how long for this frame since the first
							// frame in the
							// stream.
							// remember that IVideoPicture and IAudioSamples
							// timestamps are
							// always in MICROSECONDS,
							// so we divide by 1000 to get milliseconds.
							long millisecondsStreamTimeSinceStartOfVideo = (picture.getTimeStamp() - firstTimestampInStream) / 1000;

							// System.out.println(picture.getTimeStamp());

							final long millisecondsTolerance = 50; // and we
																	// give
																	// ourselfs
																	// 50 ms of
																	// tolerance
							final long millisecondsToSleep = (millisecondsStreamTimeSinceStartOfVideo - (millisecondsClockTimeSinceStartofVideo + millisecondsTolerance));
							if (millisecondsToSleep > 0) {
								try {
									Thread.sleep(millisecondsToSleep);
								} catch (InterruptedException e) {
									// we might get this when the user closes
									// the dialog box, so
									// just return from the method.
									return;
								}
							}
						}

						// And finally, convert the BGR24 to an Java buffered
						// image
						videoFrame = Utils.videoPictureToImage(newPic);

						// and display it on the Java Swing window
						this.repaint();
					}
				}
			} else {
				/*
				 * This packet isn't part of our video stream, so we just
				 * silently drop it.
				 */
				do {
				} while (false);
			}

		}
		Debug.log("Video finished at " + System.currentTimeMillis(), Debug.SYSTEM_LEVEL);
		/*
		 * Technically since we're exiting anyway, these will be cleaned up by
		 * the garbage collector... but because we're nice people and want to be
		 * invited places for Christmas, we're going to show how to clean up.
		 */
		if (videoCoder != null) {
			videoCoder.close();
			videoCoder = null;
		}
		if (container != null) {
			container.close();
			container = null;
		}

		synchronized (ScheduleController.getVideoLock()) {
			ScheduleController.getVideoLock().notifyAll();
		}
	}

	public boolean willBeRemoved() {
		return this.willBeRemoved;
	}

	@Override
	public boolean isSuccessfullyInitialized() {
		// TODO Auto-generated method stub
		return isSuccessfullyInitialized;
	}

}
